﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;

namespace vAddins
{
    /// <summary>
    /// Provides static extension methods for extracting objects from assemblies.
    /// </summary>
    public static class Extractor
    {
        public static Stream ExtractResource(this Assembly a, string name)
        {
            return a.GetManifestResourceStream(a.GetName().Name + "." + name);
        }

        /// <summary>
        /// Looks for all the classes in the assembly that have T as a base class/struct/interface, creates an instance of them with no constructor arguments and returns it in an array.
        /// </summary>
        /// <typeparam name="T">The base type to look for. Can be anything! (struct, class or interface)</typeparam>
        /// <param name="a">The assembly in which to look for types.</param>
        /// <returns>A list of new instances of the types found</returns>
        public static IEnumerable<T> ExtractNewObjectsOfType<T>(this Assembly a)
        {
            List<T> res = new List<T>();

            if (a == null) return res;

            foreach (Type type in a.GetExportedTypes())
                if (typeof(T).IsInterface)
                {
                    if (type.GetInterfaces().Contains(typeof(T)))
                    {
                        ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);

                        if (constructor != null)
                        {
                            T o = (T)constructor.Invoke(null);

                            if (o != null) { res.Add(o); }
                        }
                    }
                }
                else
                {
                    if (type.IsSubclassOf(typeof(T)))
                    {
                        ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);

                        if (constructor != null)
                        {
                            T o = (T)constructor.Invoke(null);

                            if (o != null) res.Add(o);
                        }
                    }
                }

            return res;
        }

        /// <summary>
        /// Looks for all the classes in the assembly that have T as a base class/struct/interface, creates an instance of them with the specified constructor types and arguments and returns it in an array.
        /// </summary>
        /// <typeparam name="T">The base type to look for. Can be anything! (struct, class or interface)</typeparam>
        /// <param name="a">The assembly in which to look for types.</param>
        /// <param name="constructorTypes">An array of the object types to look for in the constructors.</param>
        /// <param name="constructorArguments">The arguments to pass to the default constructor, if found.</param>
        /// <returns>A list of new instances of the types found</returns>
        public static List<T> ExtractNewObjectsOfType<T>(this Assembly a, Type[] constructorTypes, object[] constructorArguments)
        {
            List<T> res = new List<T>();

            if (a == null) return res;

            foreach (Type type in a.GetExportedTypes())
                if (typeof(T).IsInterface)
                {
                    foreach (Type iface in type.GetInterfaces())
                        if (iface == typeof(T))
                        {
                            ConstructorInfo constructor = type.GetConstructor(constructorTypes);

                            if (constructor != null)
                            {
                                T o = (T)constructor.Invoke(constructorArguments);

                                if (o != null) { res.Add(o); }
                            }
                        }
                }
                else
                {
                    if (type.IsSubclassOf(typeof(T)))
                    {
                        ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);

                        if (constructor != null)
                        {
                            T o = (T)constructor.Invoke(null);

                            if (o != null) res.Add(o);
                        }
                    }
                }

            return res;
        }
    }
}
